<?PHP
/*
 * $Id: barge-json.classes.php 138 2009-12-16 15:28:56Z namba0219 $
 * =============================================================================
 * Barge - Web application framework on PHP5
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 * =============================================================================
 * For more information, see http://barge.googlecode.com/
 */
namespace barge\json;

use \Exception;

use barge\System;
use barge\logging\LogFactory;
use barge\utils\ArrayList;
use barge\utils\HashMap;
use barge\utils\BeanUtils;
use barge\utils\Date;
use barge\utils\Timestamp;
use barge\utils\String;
use barge\utils\StringBuffer;


interface JSONSerializable {}

/**
 *
 * @see Exception
 */
class JSONUnserializableException extends Exception
{}


/**
 * JSONパーサー
 * <p/>
 * スカラー、配列、barge オブジェクトなどをJSON形式に変換できます。
 *
 * <pre>
 * $obj = new Item();
 * $serializer = new JsonSerializer();
 * echo $serializer->serialize($obj);
 * </pre>
 *
 * @version 1.0 2007/10/10
 * @author Kenji Namba
 */
class JSONParser
{
	// TODO :
}

/**
 * JSONシリアライザ
 * <p/>
 * スカラー、配列、barge オブジェクトなどをJSON形式に変換できます。
 *
 * <pre>
 * $obj = new Item();
 * $serializer = new JsonSerializer();
 * echo $serializer->serialize($obj);
 * </pre>
 *
 * @version 1.0 2007/10/10
 * @author Kenji Namba
 */
class JSONSerializer
{
	/**
	 * 文字エンコーディング
	 * @var string
	 */
	private $encoding = "utf-8";

	/**
	 * barge::utils::Timestamp に対するフォーマット
	 * @var string
	 */
	private $timestampFormat = "Y-m-d H:i:s";

	/**
	 * barge::utils::Date に対するフォーマット
	 * @var string
	 */
	private $dateFormat = "Y-m-d";

	/**
	 * シリアライズの実行
	 *
	 * @param object $obj 変換するオブジェクト
	 * @return string JSON形式の文字列
	 * @throws JSONUnserializableException シリアライズできない要素が含まれていた場合
	 * 			にスローされる
	 */
	public function serialize($obj)
	{
		if ($obj === null)
			return "null";
		$buf = new StringBuffer();
		$this->_serialize($buf, $obj);
		return $buf->toString();
	}

	/**
	 * @param StringBuffer $sb
	 * @param mixed $obj
	 */
	private function _serialize(StringBuffer $sb, $obj)
	{
		if ($obj === null) {
			$sb->append("null");
		} else if ((is_array($obj) || $obj instanceof ArrayList) && (isset($obj[0]) || count($obj) === 0)) {
			$sb->append("[");
			$firstDone = false;
			foreach ($obj as $colObj) {
				if ($firstDone) {
					$sb->append(",");
				} else {
					$firstDone = true;
				}
				$this->_serialize($sb, $colObj);
			}
			$sb->append("]");
		} else if ($obj instanceof HashMap || is_array($obj)) {
			$sb->append("{");
			$firstDone = false;
			foreach ($obj as $key=>$value) {
				if ($firstDone) {
					$sb->append(",");
				} else {
					$firstDone = true;
				}
				if (is_string($key)) {
					$sb->append('"');
					$sb->append($key);
					$sb->append('"');
				} else {
					$message = "";
					throw new JSONUnserializableException($message);
				}
				$sb->append(":");
				$this->_serialize($sb, $value);
			}
			$sb->append("}");
		} else if (is_string($obj)) {
			$sb->append('"');
			$sb->append($this->replaceSpecial($obj));
			$sb->append('"');
		} else if (is_int($obj) || is_float($obj) || is_double($obj)) {
			$sb->append(strval($obj));
		} else if (is_bool($obj)) {
			$sb->append($obj ? 'true' : 'false');
		} else if ($obj instanceof Date) {
			$sb->append('"');
			$sb->append($this->replaceSpecial($obj->format($this->dateFormat)));
			$sb->append('"');
		} else if ($obj instanceof Timestamp) {
			$sb->append('"');
			$sb->append($this->replaceSpecial($obj->format($this->timestampFormat)));
			$sb->append('"');
		} else {
			$sb->append('{');
			$descriptors = BeanUtils::getPropertyDescriptors($obj);
			$firstDone = false;
			foreach ($descriptors as $descriptor) {	/* @var $descriptor PropertyDescriptor */
				if ($descriptor->isReadable() ) {
					if ($firstDone) {
						$sb->append(",");
					} else {
						$firstDone = true;
					}
					$sb->append('"');
					$sb->append($descriptor->getName());
					$sb->append('":');
					$value = $descriptor->getReader()->read($obj);
					$this->_serialize($sb, $value);
				}
			}
			$sb->append('}');
//		} else {
//			$message = "Cant serialize class:" + get_class($obj);
//			throw new UnserializableException($message);
		}
	}

	/**
	 * Escape special charactors.
	 * @param source
	 * @return string
	 */
	private function replaceSpecial($source)
	{
		if ($source === null) {
			return "null";
		}
		return preg_replace(
			Array("/\n/", "/\r/", "/\"/"),
			Array('\n', '\r', '\"'),
			$source);
	}

	/**
	 * Set date pattern
	 * @param string $dateForamt
	 */
	public function setDateFormat($dateFormat)
	{
		$this->dateFormat = $dateFormat;
	}

	/**
	 * Set timestamp pattern
	 * @param string $dateForamt
	 */
	public function setTimestampForamt($dateFormat)
	{
		$this->timestampFormat = $dateFormat;
	}

}

?>